package com.anandsoftware.system;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.anandsoftware.system.domain.SystemInformation;

public class SystemInformationUtill {

	private final static Logger LOG = LoggerFactory
			.getLogger(SystemInformationUtill.class);

	public static enum SystemInformationFilter {
		ONLY_MACIDS, ONLY_OS_SERIAL, ONLY_PROCESSOR_INFORMATION, BOTH_MAC_AND_OS_SERIAL;
	}

	private static SystemInformation systemInformation;

	static {
		systemInformation = new SystemInformation();
	}

	/**
	 * 
	 * @param filter
	 *            filter to be used for limited retrival of
	 *            {@link SystemInformation}
	 * @return SystemInformation
	 */
	public static SystemInformation getSystemInformation(
			SystemInformationFilter filter) {
		LOG.info("Getting SysremInformation, FIlter Type - " + filter);
		switch (filter) {
		case BOTH_MAC_AND_OS_SERIAL:
			try {
				Set<String> macIds = getMacAddressList();
				systemInformation.setMacIdList(macIds);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				String osKey = getOsDriveSerialNumber();
				systemInformation.setOsDriveSerialNumber(osKey);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			return systemInformation;

		case ONLY_MACIDS:
			try {
				Set<String> macIds = getMacAddressList();
				systemInformation.setMacIdList(macIds);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return systemInformation;

		case ONLY_OS_SERIAL:
			try {
				String osKey = getOsDriveSerialNumber();
				systemInformation.setOsDriveSerialNumber(osKey);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return systemInformation;

		case ONLY_PROCESSOR_INFORMATION:
			try {
				fillOsAndProcessorInformation(systemInformation);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return systemInformation;

		default:
			return systemInformation;
		}
	}

	/**
	 * 
	 * @return systemInformation
	 */
	public static SystemInformation getSystemInformation() {
		LOG.info("Getting SysremInformation");
		try {
			systemInformation.setMacIdList(getMacAddressList());
		} catch (IOException e) {
			LOG.error("can't read Mac Ids");
		}

		try {
			systemInformation.setOsDriveSerialNumber(getOsDriveSerialNumber());
		} catch (IOException e) {
			LOG.error("can't read Hdd Serial Key");
		}

		try {
			fillOsAndProcessorInformation(systemInformation);
		} catch (IOException e) {
			LOG.error("can't fill os and processor information");
		}
		return systemInformation;
	}

	/**
	 * 
	 * @return set of mac Addresses fetched from system
	 * @throws IOException
	 */
	public static Set<String> getMacAddressList() throws IOException {
		Set<String> macAddress = new HashSet<String>();
		String command = "ipconfig /all";
		Process pid = Runtime.getRuntime().exec(command);
		BufferedReader in = new BufferedReader(new InputStreamReader(
				pid.getInputStream()));

		while (true) {
			String line = in.readLine();
			if (line == null)
				break;
			Pattern p = Pattern.compile(".*Physical Address.*: (.*)");
			Matcher m = p.matcher(line);
			if (m.matches()) {
				macAddress.add(m.group(1).trim());
			}
		}
		in.close();
		return macAddress;
	}

	/**
	 * 
	 * @return os Serial number from a system
	 * @throws IOException
	 */
	public static String getOsDriveSerialNumber() throws IOException {
		String osDriveSerialNumber = "";
		String command = "cmd /c vol";
		Process pid = Runtime.getRuntime().exec(command);
		BufferedReader in = new BufferedReader(new InputStreamReader(
				pid.getInputStream()));

		while (true) {
			String line = in.readLine();
			if (line == null)
				break;
			Pattern p = Pattern.compile(".*Volume Serial Number is (.*)");
			Matcher m = p.matcher(line);
			if (m.matches()) {
				osDriveSerialNumber = m.group(1).trim();
			}
		}
		in.close();
		return osDriveSerialNumber;
	}

	/**
	 * 
	 * @param systemInformation
	 *            systemInformation object refrence to be filled with a fetched
	 *            processor related information.
	 * @throws IOException
	 */
	public static void fillOsAndProcessorInformation(
			SystemInformation systemInformation) throws IOException {
		String command = "systeminfo";
		Process pid = Runtime.getRuntime().exec(command);
		BufferedReader in = new BufferedReader(new InputStreamReader(
				pid.getInputStream()));

		while (true) {
			String line = in.readLine();
			if (line == null)
				break;
			Pattern productIdPattern = Pattern.compile(".*Product ID: (.*)");
			Pattern osNamePattern = Pattern.compile(".*OS Name: (.*)");
			Pattern originalInstallDatePattern = Pattern
					.compile(".*Original Install Date: (.*)");
			Pattern systemTypePattern = Pattern.compile(".*System type: (.*)");
			Pattern osVersionPattern = Pattern.compile(".*OS Version: (.*)");
			Pattern physicalMemoryPattern = Pattern
					.compile(".*Total Physical Memory: (.*)");

			Matcher m = productIdPattern.matcher(line);
			if (m.matches()) {
				systemInformation.setOsProductKey(m.group(1).trim());
			}

			m = osNamePattern.matcher(line);
			if (m.matches()) {
				systemInformation.setOsName(m.group(1).trim());
			}

			m = originalInstallDatePattern.matcher(line);
			if (m.matches()) {
				systemInformation.setOsOriginalInstallDate(m.group(1).trim());
			}

			m = systemTypePattern.matcher(line);
			if (m.matches()) {
				systemInformation.setSystemType(m.group(1).trim());
			}

			m = osVersionPattern.matcher(line);
			if (m.matches()) {
				systemInformation.setOsVersion(m.group(1).trim());
			}

			m = physicalMemoryPattern.matcher(line);
			if (m.matches()) {
				systemInformation.setPhysicalMemory(m.group(1).trim());
			}
		}
		in.close();
	}

}
